/*oOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoO*/
/*
This function contains a simple implementation of an Input Manager, useful
for testing. It accepts data from a text file, where each line describes one request. 

The structure of each line is of one of the following two types:
(1) < Transaction Action String > < Product ID > < Cost > < User ID >
where:
 < Transaction Action String > = one of the strings defined in OpStrings[] (see ahead),
                                 where the action is either buy or sell.
 < Product ID > = a string that contains a value of type TProductID
 < Cost > = a string that contains a value of type TMoney
 < User ID > = a string that contains a value of tyep TUserID

(2) < Inventory Action String > < Filename >
where:
 < Inventory Action String >  = one of the strings defined in OpStrings[] (see ahead),
                                 where the action is either save inventory data to file, 
                                 or accept inventory from storage.
 < Filename > = the name of the inventory file.

a few lines for example of a requests file:

    load InitialInventory.inv
    in 100 451.5 6000
    in 500 14358.75 6001
    out 100 315.00 6002
    out 250 12032.50 6002
    save Inventory1.inv

Created by Amnon Drory for the course "Introduction To Systems Programming", 
School of Electrical Engineering, Tel-Aviv University. winter 2012.
*/
/*oOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoO*/

#define _CRT_SECURE_NO_WARNINGS /* to disable some Visual Studio 2010 warnings */ 

/*oOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoO*/

#include "Hw1InputManager.h"
#include "Hw1TextFileReader.h"
//#include <stdlib.h> /* for NULL */
#include <assert.h>
#include <string.h>
#include <stdio.h>

/*oOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoO*/

#define STRINGS_ARE_IDENTICAL( Str1, Str2 ) ( strcmp( (Str1),(Str2) ) == 0 )

/*oOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoO*/

/*************/
/* Constants */
/*************/

static const int BUY_SELL_COMMAND_WORDS_IN_LINE = 4;
static const int LOAD_SAVE_COMMAND_WORDS_IN_LINE = 2;

const char* const OpStrings[] = { 
    "out",            // IM_SELL_TO_CUSTOMER
    "in",            // IM_BUY_FROM_SUPPLIER
    "save",            // IM_CREATE_INVENTORY_FILE
    "load" };        // IM_ADD_INVENTORY_FROM_FILE   

/*oOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoO*/

/***********/
/* Globals */
/***********/
static int CurLine;
static TextFileReader Requests;

/*oOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoO*/

/**********************************/
/* Internal Function declarations */ 
/**********************************/
/**
 * Reads the data from the current line in 'Requests', and returns it in 
 * the relevant fields of a TRequest struct. 
 *
 * Inputs:
 * ------
 * Operation - controls how to read the data in the current line, and to which 
 *               fields in the TRequest struct it should be written.
 */
static TRequest Parse( TOperation Operation );

/**
 * Reads from the current line in 'Requests' the fields relevant for
 * the IM_SELL_TO_CUSTOMER and IM_BUY_FROM_SUPPLIER commands. 
 */
static void ParseBuySellRequestFields( TRequest* RequestPtr ); // [OUTPUT]

/**
 * Reads from the current line in 'Requests' the fields relevant for
 * the IM_CREATE_INVENTORY_FILE and IM_ADD_INVENTORY_FROM_FILE    commands. 
 */ 
static void ParseInventoryRequestFields( TRequest* RequestPtr ); // [OUTPUT]

/**
 * Advances 'CurLine', if necessary, to skip empty lines. 
 */ 
static void SkipEmptyLines();

/*oOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoO*/

/********************************/
/* Implementations of Functions */ 
/********************************/

void InptMgr_Initialize( const char* SetupString )
{
    /* Make sure OpStrings[] is defined in accordance to TOperation */
    assert( IM_SELL_TO_CUSTOMER             == 0 );
    assert( IM_BUY_FROM_SUPPLIER            == 1 );
    assert( IM_CREATE_INVENTORY_FILE        == 2 );
    assert( IM_ADD_INVENTORY_FROM_FILE      == 3 );

    /* Initialize Static Global data */ 
    Requests = CreateTextFileReader( SetupString );
	if ( Requests.IsInitialized == FALSE )
	{ printf( "Error in InptMgr_Initialize(): File %s could not be read.\n", SetupString ); exit(1); }
    CurLine = 0;
}

/*o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.*/

TRequest InptMgr_GetNextRequest()
{
    TOperation Operation;
    TRequest Request = { 
		/* Filename = */ NULL, 
		/* Operation = */ IM_NO_OPERATION } ;

    SkipEmptyLines();    

    if ( CurLine < Requests.NumOfLines )
    {
        for (   Operation = FIRST_OPERATION; 
                Operation < NUM_OF_INPUT_OPERATIONS; 
                Operation++ )
        {
            if ( STRINGS_ARE_IDENTICAL( OpStrings[Operation], Requests.WordsArr[CurLine][0] ) )
            {
                Request = Parse( Operation );
            }
        }
    }
    else { Request.Operation = IM_END_OF_INPUT; }    

    if ( Request.Operation == IM_NO_OPERATION )
    { 
        printf( "Error in InptMgr_GetNextRequest(): unknown Operation: \"%s\"\n", 
            Requests.WordsArr[CurLine][0] ); 
        exit(1);
    }

    CurLine++;

    return Request;
}

/*o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.*/

void InptMgr_CleanUp()
{
    DeleteTextFileReader( Requests );
}

/*o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.*/

static TRequest Parse( TOperation Operation )
{
    TRequest Request = { NULL };
    Request.Operation = Operation;

    switch( Operation )
    {
    case IM_SELL_TO_CUSTOMER:
    case IM_BUY_FROM_SUPPLIER:
        ParseBuySellRequestFields( &Request );
        break;
    case IM_CREATE_INVENTORY_FILE:
    case IM_ADD_INVENTORY_FROM_FILE   :
        ParseInventoryRequestFields( &Request );
        break;
    }

    return Request;
}

/*o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.*/

static void ParseBuySellRequestFields( TRequest* RequestPtr ) // [OUTPUT]
{
    if ( Requests.WordsInLine[CurLine] < BUY_SELL_COMMAND_WORDS_IN_LINE )
    { printf("Error in ParseBuySellRequestFields(): Insufficient words in line %d\n", CurLine ); exit(1); }

    RequestPtr->TransactionData.ProductID = String2TProductID( Requests.WordsArr[CurLine][1] );

    RequestPtr->TransactionData.Cost = String2TMoney( Requests.WordsArr[CurLine][2] );
	RequestPtr->TransactionData.UserID = String2TUserID( Requests.WordsArr[CurLine][3] );
}

/*o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.*/

static void ParseInventoryRequestFields( TRequest* RequestPtr ) // [OUTPUT]
{
    int NumOfLettersInFilename;
    int NumOfCharsInString;

    if ( Requests.WordsInLine[CurLine] < LOAD_SAVE_COMMAND_WORDS_IN_LINE )
    { printf("Error in ParseInventoryRequestFields(): Insufficient words in line %d\n", CurLine ); exit(1); }

    NumOfLettersInFilename = strlen( Requests.WordsArr[CurLine][1] );
    
    NumOfCharsInString = NumOfLettersInFilename + 1; /* leave room for terminating zero */

    RequestPtr->Filename = (char*)malloc( sizeof(char) * NumOfCharsInString );
    
    if ( RequestPtr->Filename == NULL )
    { printf("Error in ParseInventoryRequestFields(): Could not allocate memeory\n"); exit(1); }

    strncpy( RequestPtr->Filename, Requests.WordsArr[CurLine][1], NumOfLettersInFilename );

    RequestPtr->Filename[NumOfCharsInString-1] = 0; /* string-terminating zero */
}

/*o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.o.*/

static void SkipEmptyLines()
{
    while(    ( CurLine < Requests.NumOfLines ) && 
            ( Requests.WordsInLine[ CurLine ] == 0 ) )
    {
        CurLine++;
    }
}

